﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using PSE.Framework.Common;
using System.ComponentModel;

namespace PSE.Framework.Service.Common
{
    public class EnumConverter
    {
        /// <summary>
        /// Metodo responsavel por encontrar o item de enum acordo com o grupo de valores e o valor
        /// </summary>
        /// <param name="group">Grupo de valores, propriedade do atributo EnumMemberValue que deve ser aplicado nos itens de enum
        /// Através do grupo, pode-se criar grupos de EnumMemberValue, possibilitando assim que um enum seja mapeado para mais
        /// de um valor e vice-versa
        /// </param>
        /// <param name="value">Valor correspondente ao item de enum</param>
        /// <returns></returns>
        public static T Parse<T>(string group, string value)
        {
            return (T)Parse(typeof(T), group, value);
        }


        public static object Parse(Type enumerable, string value)
        {
            return Parse(enumerable, null, value);
        }


        /// <summary>
        /// Metodo responsavel por encontrar o item de enum acordo com o grupo de valores e o valor
        /// </summary>
        /// <param name="enumerable">Tipo que deve ser um enum.</param>
        /// <param name="group">Grupo de valores, propriedade do atributo EnumMemberValue que deve ser aplicado nos itens de enum
        /// Através do grupo, pode-se criar grupos de EnumMemberValue, possibilitando assim que um enum seja mapeado para mais
        /// de um valor e vice-versa
        /// </param>
        /// <param name="value">Valor correspondente ao item de enum</param>
        /// <returns></returns>
        public static object Parse(Type enumerable, string group, string value)
        {
            object[] attributes;
            Type enumType;
            
            //se for um enum 'nulavel'
            if (enumerable.IsNullable())
            {
                // Validando se Type é um Enum
                enumType = new NullableConverter(enumerable).UnderlyingType;
                if (!enumType.IsEnum)
                    throw new ArgumentException("The argument should be an enum", enumType.GetType().Name);
            }
            else
            {
                // Validando se Type é um Enum
                if (!enumerable.IsEnum)
                    throw new ArgumentException("The argument should be an enum", enumerable.GetType().Name);
                else
                    enumType = enumerable;

            }

            foreach (FieldInfo info in enumType.GetFields())
            {
                if (!info.IsSpecialName)
                {
                    attributes = info.GetCustomAttributes(typeof(EnumMemberValueAttribute), true);
                    if (attributes != null && attributes.Length > 0)
                    {
                        EnumMemberValueAttribute enumMemberValueAttribute;
                        for (int i = 0; i < attributes.Length; i++)
                        {
                            enumMemberValueAttribute = (EnumMemberValueAttribute)attributes[i];
                            if ((enumMemberValueAttribute.Group == group || String.IsNullOrEmpty(group)) &&
                                enumMemberValueAttribute.Value == value)
                                return info.GetValue(null);
                        }
                    }
                    else
                    {
                        throw new InvalidCastException("The attribute EnumMemberValueAttribute not found on the type " + enumerable.Name);
                    }
                }
            }

            //se nao encontrar o valor do item, e o retorno aceitar nulo, retornar null
            if (value == null && enumerable.IsNullable())
            {
                return null;
            }            
            //nao foi encontrado um enum para o para group/value
            throw new InvalidCastException(string.Format("Unable to convert the value to an enum of type {0}", enumerable.Name));
        }




        /// <summary>
        /// Metodo responsavel por encontrar o item de enum acordo com o valor
        /// </summary>
        /// <param name="value">Valor correspondente ao item de enum</param>
        /// <returns></returns>
        public static T Parse<T>(string value)
        {
            return EnumConverter.Parse<T>(null, value);
        }

        /// <summary>
        /// Metodo responsavel por converter um item de enum em uma string de acordo com o grupo de valores
        /// informador
        /// </summary>
        /// <param name="enumerable">Item de enum a ser convertido</param>
        /// <param name="group">Grupo de valores, propriedade do atributo EnumMemberValue que deve ser aplicado nos itens de enum
        /// Através do grupo, pode-se criar grupos de EnumMemberValue, possibilitando assim que um enum seja mapeado para mais
        /// de um valor e vice-versa</param>
        /// <returns></returns>
        public static string ToString<T>(T enumerable, string group)
        {
            return ToString((object)enumerable, group);
        }

        public static string ToString(object enumerable)
        {
            return ToString(enumerable, null);
        }


        /// <summary>
        /// Metodo responsavel por converter um item de enum em uma string de acordo com o grupo de valores
        /// informador
        /// </summary>
        /// <param name="enumerable">Item de enum a ser convertido</param>
        /// <param name="group">Grupo de valores, propriedade do atributo EnumMemberValue que deve ser aplicado nos itens de enum
        /// Através do grupo, pode-se criar grupos de EnumMemberValue, possibilitando assim que um enum seja mapeado para mais
        /// de um valor e vice-versa</param>
        /// <returns></returns>
        public static string ToString(object enumerable, string group)
        {
            if ((object)enumerable == null)
                return null;
            Type t = enumerable.GetType();
            object[] attributes;
            foreach (FieldInfo info in t.GetFields())
            {
                //ignorar itens não declarados diretamente no enum
                if (!info.IsSpecialName)
                {
                    if (enumerable.ToString() == info.GetValue(null).ToString())
                    {
                        attributes = info.GetCustomAttributes(typeof(EnumMemberValueAttribute), true);
                        if (attributes != null && attributes.Length > 0)
                        {
                            //procurar o valor default
                            foreach (EnumMemberValueAttribute att in attributes)
                            {
                                if (att.Group == group ||
                                    (String.IsNullOrEmpty(att.Group) && String.IsNullOrEmpty(group)))
                                {
                                    return att.Value;
                                }
                            }
                        }
                        else
                        {
                            throw new InvalidCastException("The attribute EnumMemberValueAttribute not found on the type " + t.Name);
                        }
                    }
                }
            }

            throw new InvalidCastException("Unable to find a value mapped to the enum");
        }

        /// <summary>
        /// Metodo responsavel por converter um item de enum em uma string assumindo
        /// o grupo default (null)
        /// </summary>
        /// <param name="enumerable">Item de enum a ser convertido</param>
        /// <returns></returns>
        public static string ToString<T>(T enumerable)
        {
            return ToString(enumerable, null);
        }
    }

    //public class EnumConverter
    //{
    //    public static T Parse<T>(string group, string value)
    //        where T : struct
    //    {
    //        object[] attributes;

    //        // Validando se Type é um Enum
    //        if (!typeof(T).IsEnum)
    //            throw new ArgumentException("The argument should be an enum", "T");

    //        foreach (FieldInfo info in typeof(T).GetFields())
    //        {
    //            attributes = info.GetCustomAttributes(typeof(EnumMemberValueAttribute), true);
    //            if (attributes != null)
    //            {
    //                EnumMemberValueAttribute enumMemberValueAttribute;
    //                for (int i = 0; i < attributes.Length; i++)
    //                {
    //                    enumMemberValueAttribute = (EnumMemberValueAttribute)attributes[i];
    //                    if ((enumMemberValueAttribute.Group == group || String.IsNullOrEmpty(group)) &&
    //                        enumMemberValueAttribute.Value == value)
    //                        return (T)info.GetValue(null);
    //                }
    //            }
    //        }

    //        //nao foi encontrado um enum para o para group/value
    //        throw new InvalidCastException(string.Format("Unable to convert the value to an enum of type {0}", typeof(T).Name));
    //        //throw new ArgumentException("Não foi possível recuperar o item do enum relacionado ao valor informado.", "value");
    //    }

    //    public static T Parse<T>(string value)
    //        where T : struct
    //    {
    //        return EnumConverter.Parse<T>(null, value);
    //    }

    //    public static string ToString(System.Enum enumerable, string group)
    //    {
    //        if (enumerable == null)
    //            return null;

    //        Type t = enumerable.GetType();
    //        object[] attributes;
    //        foreach (FieldInfo info in t.GetFields())
    //        {
    //            //ignorar itens não declarados diretamente no enum
    //            if (!info.IsSpecialName)
    //            {
    //                if (enumerable.ToString() == info.GetValue(null).ToString())
    //                {
    //                    attributes = info.GetCustomAttributes(typeof(EnumMemberValueAttribute), true);
    //                    if (attributes != null && attributes.Length > 0)
    //                    {
    //                        //procurar o valor default
    //                        foreach (EnumMemberValueAttribute att in attributes)
    //                        {
    //                            if (att.Group == group ||
    //                                (String.IsNullOrEmpty(att.Group) && String.IsNullOrEmpty(group)))
    //                            {
    //                                return att.Value;
    //                            }
    //                        }
    //                    }
    //                }
    //            }
    //        }

    //        throw new InvalidCastException("Unable to find a value mapped to the enum");            
    //    }
    //}
}
